﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Data;
using System.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Security;
using System.Threading;
using System.ServiceModel;
using Microsoft.Practices.EnterpriseLibrary.ExceptionHandling;
using Infrastructure.Aspect.ExceptionHandling;

namespace Infrastructure.Services.Behaviors
{
    public class SecurityLogOpeBehavior : IOperationBehavior
    {


        public void AddBindingParameters(OperationDescription operationDescription, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
        {
            
        }

        public void ApplyClientBehavior(OperationDescription operationDescription, System.ServiceModel.Dispatcher.ClientOperation clientOperation)
        {
            
        }

        public void ApplyDispatchBehavior(OperationDescription operationDescription, System.ServiceModel.Dispatcher.DispatchOperation dispatchOperation)
        {
            dispatchOperation.Invoker = new SecurityLogOperationInvoker(dispatchOperation.Action,  operationDescription.Name, dispatchOperation.Invoker);
        }

        public void Validate(OperationDescription operationDescription)
        {
            
        }
        
    }
    public class SecurityLogOperationInvoker : IOperationInvoker
    {
        string operationName;
        string actionName;
        IOperationInvoker originalInvoker;
        public SecurityLogOperationInvoker(string action, string opename, IOperationInvoker original)
        {
            this.actionName = action; 
            this.operationName = opename;
            this.originalInvoker = original;
        }       
        public object[] AllocateInputs()
        {
            return this.originalInvoker.AllocateInputs();
        }
        public void Validate()
        {
            try
            {
                var AspectDatabase = EnterpriseLibraryContainer.Current.GetInstance<Database>();
                var cmd = AspectDatabase.GetSqlStringCommand("select  [Rule] from [SecurityRules] where ApplicationName = @ApplicationName and ActionName = @ActionName");
                var ApplicationName = ConfigurationManager.AppSettings["ApplicationName"];
                AspectDatabase.AddInParameter(cmd, "ApplicationName", System.Data.DbType.String, ApplicationName);
                AspectDatabase.AddInParameter(cmd, "ActionName", System.Data.DbType.String, actionName);
                var rule = AspectDatabase.ExecuteScalar(cmd) as string;
                Parser parser = new Parser();
                if (string.IsNullOrEmpty(rule))
                {
                    throw new ApplicationException("Access is denied");
                }
                else if (!parser.Parse(rule).Evaluate(Thread.CurrentPrincipal))
                {
                    throw new ApplicationException("Access is denied");
                }
            }            
            catch (Exception ex)
            {
                bool rethrow = ExceptionPolicy.HandleException(ex, ExceptionPolicyNames.ServiceInterfaceInfrastructure);
                if (rethrow)
                    throw;                
            }                     
        }
        public object Invoke(object instance, object[] inputs, out object[] outputs)
        {
            Validate();
            object result = this.originalInvoker.Invoke(instance, inputs, out outputs);            
            return result;
        }

        public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
        {
            Validate();
            return this.originalInvoker.InvokeBegin(instance, inputs, callback, state);  
        }
        public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
        {
            object operationResult = this.originalInvoker.InvokeEnd(instance, out outputs, result);            
            return operationResult;            
        }
        public bool IsSynchronous
        {
            get { return this.originalInvoker.IsSynchronous; }
        }        
    }
}
